﻿using Jacobi.Vst.Core;
using Jacobi.Vst.Framework;
using Jacobi.Vst.Framework.Plugin;
using Aleator.Core;
using Aleator.Helpers;
using System;
using System.Diagnostics;

namespace Aleator
{
    /// <summary>
    /// A dummy audio processor only used for the timing of midi processing.
    /// </summary>
    class AudioProcessor : VstPluginAudioProcessorBase, IVstPluginBypass
    {
        private const int AudioInputCount = 2;
        private const int AudioOutputCount = 2;
        private const int InitialTailSize = 0;

        public int IsPlaying;
        public static int Errors;
        public static int EventErrors;

        private Plugin _plugin;
        private MidiProcessor _midiProcessor;
        private VstTimeInfoFlags _defaultTimeInfoFlags;
        private IVstMidiProcessor _hostProcessor;
        private IVstHostSequencer _sequencer;

        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        /// <param name="plugin">Must not be null.</param>
        public AudioProcessor(Plugin plugin)
            : base(AudioInputCount, AudioOutputCount, InitialTailSize)
        {
            _plugin = plugin;
            _midiProcessor = plugin.GetInstance<MidiProcessor>();
            _timeInfo = new VstTimeInfo();

            _defaultTimeInfoFlags = VstTimeInfoFlags.ClockValid | VstTimeInfoFlags.TempoValid | VstTimeInfoFlags.TransportPlaying;
        }

        /// <inheritdoc />
        /// <remarks>This method is used to push midi events to the host.</remarks>
        public override void Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {
            _timeInfo = null;

            if (_hostProcessor == null)
            {
                _hostProcessor = _plugin.Host.GetInstance<IVstMidiProcessor>();
            }

            if (_midiProcessor != null && _hostProcessor != null && _sequencer != null)
            {
                 _timeInfo = _sequencer.GetTime(_defaultTimeInfoFlags);

                if (_midiProcessor.Events.Count == 0 && IsPlaying == 2 && !Session.Paused)
                {
                    //Logger.Log(TraceEventType.Error, "The Aleator process has generated no events.");
                    EventErrors++;
                }

                if (_midiProcessor.Events.Count > 0)
                {
                    //TODO: NullReferenceException must also be trapped.
                    try
                    {
                        _hostProcessor.Process(_midiProcessor.Events);
                        _midiProcessor.Events.Clear();
                    }
                    catch (Exception e)
                    {
                        if (Errors > 20)
                        {
                            Logger.Log(TraceEventType.Error, "The Aleator process has died.");
                            throw new ApplicationException("The Aleator process has died.");
                        }
                        else
                        {
                            Logger.Log(TraceEventType.Warning, string.Format("{0} handled by Aleator audio processor at {1}", e.GetType().ToString(), DateTime.Now.ToString("HH:mm:ss.fff")));
                            Errors++;
                        }
                    }
                }

                //Every so often getting information from DAW throws NulReferenceException. If this happens a ton of times in a row it usually means the Aleator process is dead.
                try
                {
                    if (((int)_timeInfo.Flags & (int)Jacobi.Vst.Core.VstTimeInfoFlags.TransportPlaying) != IsPlaying)
                    {
                        IsPlaying = ((int)_timeInfo.Flags & (int)Jacobi.Vst.Core.VstTimeInfoFlags.TransportPlaying);
                        _plugin.Editor.UIWrapper.Instance.ToggleButtons(IsPlaying);
                    }
                }
                catch (NullReferenceException)
                {
                    if (Errors > 20)
                    {
                        Logger.Log(TraceEventType.Error, "The Aleator process has died.");
                        throw new ApplicationException("The Aleator process has died.");
                    }
                    else
                    {
                        Logger.Log(TraceEventType.Warning, "NullReferenceException handled by Aleator audio processor at " + DateTime.Now.ToString("HH:mm:ss.fff"));
                        Errors++;
                    }
                }
            }

            // perform audio-through
            // base.Process(inChannels, outChannels);
        }


        public bool Bypass { get; set; }

        private VstTimeInfo _timeInfo;
        /// <summary>
        /// Gets the current time info.
        /// </summary>
        /// <remarks>The Time Info is refreshed with each call to Process.</remarks>
        public VstTimeInfo TimeInfo
        {
            get
            {
                if (_sequencer == null && _plugin.Host != null)
                {
                    _sequencer = _plugin.Host.GetInstance<IVstHostSequencer>();
                }

                if (_sequencer != null)
                {
                    _timeInfo = _sequencer.GetTime(_defaultTimeInfoFlags);
                }

                return _timeInfo;
            }
        }

        private static int _scrollNumber;

        /// <summary>
        /// Number of positions to scroll the synth browser
        /// </summary>
        public static int ScrollNumber
        {
            get
            {
                return _scrollNumber;
            }
            set
            {
                _scrollNumber = value;
            }
        }
    }
}
